home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Belgian Amiga Club - ADF Collection
/
BS1 part 65.zip
/
BS1 part 65
/
Delitracker v1.21.adf
/
Developer
/
Programming.doc
< prev
next >
Wrap
Text File
|
1992-05-17
|
16KB
|
522 lines
© 1992 by Delirium
Date 15.05.1992
How to write an external Player
DeliTracker supports so called external players. These are players that are in
a special format so that they can be loaded from DeliTracker. If such a player
is loaded, DeliTracker recognises & plays the moduletype that the player
supports. These external players make DeliTracker flexible. New players can be
added by the user and old players can be updated easily. The user can select
only the needed players. The current version DeliTracker can handle up to 64
external players simultaneously. This should be enough for the near future :)
When DeliTracker is started it will load all the players in the 'DeliPlayers'
directory (or the specified playerpath e.g. tooltypes or in the configfile).
In addition you can add players while DeliTracker is running.
External Players are executables which means they are relocated. At the start
of an external player you can find the characteristic playerstructure. This
structure is generated with four macros. These macros can be found in
'misc/deliplayer.i'.
It is not difficult to adapt a player if you have the replayroutine. You only
need to write some interfacecode. DeliTracker has some helpful builtin
routines that will make this job a lot easier.
1. Basics
To adapt a new soundsystem you need the replayroutine of that soundsystem, and
at least 5 modules for testing the adaption.
2. Schematics of a external player
{
Playerheader
TagArray
Interfacecode
Replaycode+Data
}
3. Functions that the external players (must) have
The PLAYERHEADER macro generates the header that identifies the file as a
valid external player for DeliTracker. This macro must exist and the player
must begin with the macro. The only parameter you must supply is a pointer
to a Tag Array that contains all functions that the external player supports.
In all calls to player functions (except the interrupt routine) a5 will
contain the address to the global player datastructure (Base). For more about
this structure read 'misc/deliplayer.i'. In your routines (except in
DTP_Interrupt) you can trash all registers.
PLAYERHEADER <tagarray>
tagarray Pointer to a tag array, terminated with TAG_DONE.
Tags with ti_Data NULL are ignored.
Here is a summary of all system tags you may use:
TAG_DONE
TAG_IGNORE
TAG_MORE
TAG_SKIP
DTP_CustomPlayer this tag identifies a player as customplayer.
ti_Data may not be NULL !
DTP_RequestDTVersion only if the DeliTracker version is greater than or
equal to the requested version (ti_Data) will
DeliTracker accept the player. If your player
uses functions that were introduced in later revisions
of DeliTracker you must set this tag according to the
version that introduced this function.
DTP_RequestV37 if this tag is set, only the Kick 2.0 version of
DeliTracker will accept the player.
(dtg_GadToolsBase is valid)
DTP_PlayerVersion Tag that contains the revision number of the player.
If there are two players with same name the player
with the higher version is used.
DTP_PlayerName ti_Data contains a pointer to the playername. This
string may be as long as you wish, but only the first
24 chars are actually used. This tag must exist !
DTP_Creator pointer to the author/adaptor name. This string is
visible in the prefs window if the player is selected.
The string may contain $A as line separator.
DTP_Check1 pointer to a module identification routine. This
routine is called after the first 1024 bytes of the
module is loaded. If the module is shorter, the rest
will contain zero. If the routine recognizes the
moduleformat it must return d0=0 else d0<>0.
DTP_Check2 pointer to a module identification routine. This
routine is called after the complete module is loaded
(and decrunched). If the routine recognizes the module
it must return d0=0 else d0<>0.
DTP_Extload pointer to a optional loadroutine for modules. If an
error occurs d0<>0 else d0=0. Please remember to free
all allocated resources (memory, locks,...), because
no further player function is called.
DTP_Interrupt pointer to a interruptroutine. This routine is called
every 1/50 sec. via a timerinterrupt. Note: your
interruptroutine is not executed in the timerinterrupt
itself. This is the standard method for gaining the
correct playspeed regardless of the videomode. If the
DTP_Faster/DTP_Slower pointers are not supplied,
DeliTracker emulates this by changing the interrupt
frequency. If this tag doesn't exist, you must
supply DTP_StartInt/DTP_StopInt.
DTP_Stop pointer to optional stop routine. If this tag is not
exist, DeliTracker uses the following standard method:
stop interrupt (DTP_StopInt)
cleanup sound (DTP_EndSnd)
reinitialize the song (DTP_InitSnd)
This routine will stop playing the song, reset the
'patterncounter' to the begin and change the playspeed
to default. This means that the interrupt is started
again and the song should begin to play from the
beginning.
DTP_Config pointer to an optional initialising routine. This
routine is only called once after the player is
loaded. Purpose:
The player could load a playerspecific configfile.
DTP_Userconfig pointer to a optional initialising routine. This
routine is called if the user selects the 'Config'
button in the prefswindow. Purpose: The player could
open a playerspecific configwindow for setting
special options (e.g instrumentpath for a sonix
player) and saving them into a configfile.
DTP_SubSongRange This tag should be supplied if the player supports
multimodules. ti_Data points to a function that
returns in d0 the minimum and in d1 the maximum
subsong number.
DTP_InitPlayer pointer to an initialising routine, that is called if
a module is loaded successfully. Must return d0=0 if
all is ok else d0<>0. The audioallocation must be done
here. (DeliTracker has a function that does the
allocation.) If the player supports subsongs it has
to set dtg_SndNum(a5) to the first subsongnumber.
DTP_EndPlayer pointer to a cleanuproutine, that is called if the
module is removed from memory. Audiochannels have to
be freed here. (Use the DeliTracker internal
supportroutine)
DTP_InitSound pointer to an optional initialising routine. This
routine has the task to (re)initialize the module. If
the interrupt is started the song should begin to play
at the beginning.
DTP_EndSound pointer to an optional cleanuproutine. For example it
can be used to reset the volumeregister or the
audio-dma.
DTP_StartInt pointer to an initialising routine, that must exist if
DTP_Interrupt doesn't exist. It has the task to start
the sound.
DTP_StopInt pointer to a cleanuproutine, that must exist if
DTP_Interrupt doesn't exist. It has the task to stop
the sound.
DTP_Volume pointer to function that sets the volume. This
function is called every time the volume is changed
(via arexx or slider) and once at the initialising
phase of the module (before DTP_InitSnd is called).
The mastervolume can be found in dtg_SndVol(a5). The
mastervolume is the highest volume allowed. The
effective volume can be calculated using the
following formula:
VOL_eff=((MASTERVOLUME*modulevolume)>>6).
See also the example sources.
DTP_Balance pointer to a function that sets the balance. This
function is called every time the balance is changed
(via arexx or slider) and once at the initialising
phase of the module (before tf_InitSnd is called).
The balance for the left channel can be found in
dtg_SndLBal(a5), for the right channel in
dtg_SndRBal(a5). Note: All players that support
balance are capable of volume too! Then you must use
the same routine for both operations. The mastervolume
for the left channels can be calculated with this
formula:
LeftMaster =((dtg_Volume(a5)*dtg_SndLBal(a5))>>6).
For the right channels the formula is similar.
DTP_Faster pointer to a function that increases the playspeed.
DTP_Slower pointer to a function that decreases the playspeed.
DTP_NextPatt pointer to a function that increases the
patternpointer.
DTP_PrevPatt pointer to a function that decreases the
patternpointer.
DTP_NextSong pointer to a function that increases the
subsongcounter (only if the subsong exists).
DTP_PrevSong pointer to a function that decreases the
subsongcounter (only if the subsong exists).
4. DeliTracker support functions that can be called from the external player.
Every function is called like this:
move.l dtg_XXX(a5),a0 ; a5 must contain the base
jsr (a0)
All functions (exept dtg_SongEnd) use d0/d1/a0/a1 as Scratchregister.
A5 must contain the base.
dtg_GetListData
SYNOPSIS
memory size = dtg_GetListData(number)
a0 d0 d0.l
FUNCTION
Returns the address and the length of a file that was loaded
with dtg_LoadFile().
INPUTS
number - number of the file beginning with 0 for the file that
was selected by the user.
RESULT
memory - startaddress of the files in memory, if error NULL.
size - length of the loaded file in bytes or 0 in case of
an error
dtg_LoadFile
SYNOPSIS
success = dtg_LoadFile(name)
FUNCTION
Loads and decrunches the specified file to chipmemory.
Note: this function automatically adds '.pp' to the filename.
INPUTS
name - store the filename in the internal buffer
(dtg_PathArray contains a pointer to this buffer)
RESULT
success - success d0.l=0, else d0.l<>0.
dtg_CopyDir
SYNOPSIS
dtg_CopyDir()
FUNCTION
Copies the directory of the selected file at the end
of the string, that dtg_PathArray points to.
dtg_CopyFile
SYNOPSIS
dtg_CopyFile()
FUNCTION
Copies the filename of the selected file at the end
of the string, that dtg_PathArray points to.
dtg_CopyString
SYNOPSIS
dtg_CopyString(string)
a0
FUNCTION
a0 contains the address of a string, which is copied at the
end of the string that dtg_PathArray points to.
INPUTS
string - a0 contains the pointer to the string
dtg_AudioAlloc
SYNOPSIS
success = dtg_AudioAlloc()
FUNCTION
Allocates the audiochannels
RESULT
success - if we got them: d0.l=0, else d0.l<>0.
dtg_AudioFree
SYNOPSIS
dtg_AudioFree()
FUNCTION
Frees the audiochannels that were allocated with
dtg_AudioAlloc.
dtg_StartInt
SYNOPSIS
dtg_StartInt()
FUNCTION
Starts the soundinterrupt. If DTP_Interrupt exists,
DeliTracker starts the internal timerinterrupt, else
DTP_StartInt is called.
dtg_StopInt
SYNOPSIS
dtg_StopInt()
FUNCTION
Stops the soundinterrupt. If DTP_Interrupt exists,
DeliTracker stops the internal timerinterrupt, else
DTP_StopInt is called.
dtg_SongEnd
SYNOPSIS
dtg_SongEnd()
FUNCTION
Signals DeliTracker, that the module was played once.
This function doesn't change any registers and is save
to call from interrupts.
dtg_CutSuffix
SYNOPSIS
dtg_CutSuffix()
FUNCTION
removes the suffix '.pp' at the end of the string, that
dtg_PathArray points to.
5. Module-Recognition
In order to recognize the different moduleformats every player contains a
specific routine, that tells DeliTracker if the player can play this module or
not. This routine has the task to check certain positions in the module that
are identical in every module (like 'M.K.' at offset $438 in NoiseTracker
modules) or significant assembler instructions (if the module contains the
player). Testing against one or two branches or jumps is NOT enough, cause
many modules with player have branchtables at the beginning of the module. If
the player recognizes the wrong module, a system crash will be the result! So
be careful with the Ckeckroutine. As you can see the playerstructure has two
checkfunctions but the player must use exactly one check routine. This leads
to two basic player types:
1.Type one Player
Here the Check1 function is implemented.
Advantage: You can implement players that can load the module itself.
Disadvantage: No Packsupport, more complex.
This type should only be used if you REALLY need to load the module by
yourself!
(e.g. FTM, IFF-8SVX player that loads the sample while playing, ...)
2. Type two Player - the easier way
Here the Check2 function is implemented.
Advantage: The module may be packed and DeliTracker handles the
loading and allocation of the module for you.
Disadvantage: The module is completely loaded into CHIP-Memory.
Regardless of the playertype the checkfunction must return d0.l=0 if the
player can handle this module or d0.l<>0 if not.
6. Player-Interrupt
Players can be divided in two categories:
1. Player that uses the internal timer interrupt from DeliTracker
Advantage: The player is independent from the selected videomode
The player has automatically the faster/slower function.
No expense for interrupthandling (interrupt structure
and the insert/remove code).
Compatible with the serial.device.
Disadvantage: The interrupt is not synchronous to the VBlank (This leads
to problems only to certain cases).
2. Player that generates their own interrupt.
Advantage: You can use other interrupt sources (like AudioIRQ)
Disadvantage: You have to handle the interrupts by yourself, and if
you use VBlank the player is not independent to the
videomode.
If you use your own timerinterrupt you should allocate CIAB because CIAA
is used from the system under 2.x. It is wise not to run the soundcode
directly in the timerinterrupt. Instead you should Cause() a SoftInt in
the timer interrupt and execute the routine in the SoftInt. This is to
assure maximum compatibility for modem users because the SoftInt has a
lower priority thant the RBF interrupt. Beware of writing directly to the
680x0 intvectors! You should use AddIntServer() or SetIntVector() from
the OS.
7. Custom Modules
These are not modules in the conventional meaning. They are more like
external players, the difference is that custom modules contain the
player AND the module. With the custom module interface you can adapt
almost every module. If you have more modules with the same replay
routine we suggest to write an own player for this moduleformat.
Schematic of a custom module (custom player)
{
Playerheader
TagArray
Interfacecode
Replaycode+Data
MODULE
}
The Tag DTP_CustomPlayer identifies a player as a custom module.
The following Tags are ignored for custommodules:
- DTP_PlayerVersion
- DTP_PlayerName
- DTP_Creator
- DTP_Check1
- DTP_Check2
- DTP_ExtLoad
- DTP_Config
- DTP_UserConfig
8. Checklist
This is a small list that you should match when you create your own player or
custom module.
[ ] checkroutine exact enough ?
[ ] audiochannel allocated/freed correctly ?
[ ] all allocated memory freed after playing?
[ ] all locks unlocked after playing ?
[ ] enforcer and mungwall proof ?
[ ] viable error handling path taken for ALL possible errors ?
[ ] player tested under 1.3 and 2.0 ?
[ ] does the player work correct in all videomodes ?
9. Other things
The player should not change the LED condition because DeliTracker will handle
it.
Problems
Symptom possible source elimination
crash a5 trashed, other registers
not saved, wrong stack usage
initialization wrong or too late
module too new for the replayroutine better check
module sounds Audio data not in chipmem
wrong wrong initialization
module too new for the replayroutine
player needs some special values
in some registers extra init code
wrong videomode
no sound Audio-DMA off :-)
>68000 player writes directly to processor use OS routines to
intvectors and VBR is not 0. set IntVectors
no free wrong interrupt handling VBlank-IRQ:
CPU-time Z-Flag must be set
at the end of the
interruptroutine.
slow system 68000/020/030 toooo slow buy a 68040 or
CRAY ;-)